my_proto = Proto ("sdtp_v3","sdtp v3","sdtp version 3.0")

local msgAry = { [0x0001] = "verNego_Req",[0x8001] = "verNego_Resp",
 [0x0002] = "linkAuth_Req", [0x8002] = "linkAuth_Resp",
 [0x0003] = "linkCheck_Req", [0x8003] = "linkCheck_Resp",
 [0x0004] = "linkRel_Req", [0x8004] = "linkRel_Resp",
 [0x0005] = "notifySignalData_Req", [0x8005] = "notifySignalData_Resp",
 [0x0008] = "SCGIDInform_Req",[0x8008] = "SCGIDInform_Resp",
 [0x0009] = "statusNotification_Req",[0x8009] = "statusNotification_Resp",
 [0x000A] = "alarmInform_Req",[0x800A] = "alarmInform_Resp",
 [0x000B] = "linkInform_Req",[0x800B] = "linkInform_Resp",
 [0x000C] = "subscribeData_Req",[0x800C] = "subscribeData_Resp",
 [0x000D] = "linkReport_Req",[0x800D] = "linkReport_Resp",
 [0x000E] = "compressNego_Req",[0x800E] = "compressNego_Resp"}
 
local linktypeAry = { [0x01] = "2M",[0x02] = "64K", [0x03] = "IPv4", [0x04] = "ATM", [0x05] = "Abis"}
local cardtypeAry = { [0x01] = "FEGE",[0x02] = "E1", [0x03] = "ATM", [0x04] = "STM", [0x05] = "BSC", [0x06] = "LTE SoftCap", [0x0F] = "Other"}
local abisRateAry = { [0x00] = "2Mbps",[0x01] = "16Kbps",[0x02] = "32Kbps", [0x03] = "56Kbps", [0x04] = "64Kbps"}
local abisPortTypeAry = { [0x00] = "E1",[0x01] = "155M", [0x02] = "622M", [0x03] = "2.5G", [0x04] = "10G"}
local abisDirectionAry = { [0x00] = "UPLINK",[0x01] = "DOWNLINK"}
local directionAry = { [0x00] = "no diretion",[0x01] = "Tx",[0x02] = "Rx"}
local netElement = { [0x01] = "ENODEB",[0x02] = "MME", [0x03] = "SGW", [0x04] = "PGW", [0x05] = "PCRF", [0x06] = "HSS", [0x0F] = "Reserve"}
local interface = { [0x01] = "Uu",[0x02] = "Uu-Exterd", [0x03] = "X2", [0x04] = "S1-MME", [0x05] = "S6a", [0x06] = "S10", [0x07] = "s11",
					[0x08] = "S5/S8",[0x09] = "Gn/Gp", [0x0a] = "Gx", [0x0b] = "SGs", [0x0c] = "S3", [0x0d] = "S4", [0x0e] = "s12",
					[0x0f] = "S6d",[0x10] = "S1-u",[0x11] = "SGi"}
local channelTypeArr = { [0x00] = "BCCH-BCH-Message",[0x01] = "BCCH-DL-SCH-Message",[0x02] = "MCCH-Message", [0x03] = "PCCH-Message", [0x04] = "DL-CCCH-Message", [0x05] = "DL-DCCH-Message", [0x06] = "UL-CCCH-Message", [0x07] = "UL-DCCH-Message"}


local f_usMsgLen = ProtoField.uint16("TotalLength", "TotalLength", base.DEC)
local f_usMsgType = ProtoField.uint16("SDTPMsgType", "SDTP Message Type", base.HEX,msgAry)
local f_SequenceId = ProtoField.uint32("SequenceId", "SequenceId", base.DEC)
local f_TotalContents = ProtoField.uint8("TotalContents", "TotalContents", base.DEC)
local f_CompressFlag = ProtoField.uint8("CompressFlag", "CompressFlag", base.DEC)

local f_usPktLen = ProtoField.uint16("PktLen", "PktLen", base.DEC)
local f_btPktLen = ProtoField.uint8("SpecialHeaderLen", "SpecialHeaderLen", base.DEC)
local f_usVersion = ProtoField.uint8("Ver", "Version", base.DEC)
local f_usLinkType = ProtoField.uint8("LinkType", "LinkType", base.DEC,linktypeAry)
local f_usCardType = ProtoField.uint8("CardType", "CardType", base.DEC,cardtypeAry)
local f_NetElement = ProtoField.uint8("NetElement", "NetElement", base.DEC,netElement)
local f_Interface = ProtoField.uint8("Interface", "Interface", base.DEC,interface)
local f_Direction = ProtoField.uint8("Direction", "Direction", base.DEC,directionAry)
local f_usCardID = ProtoField.uint8("CardID", "CardID", base.DEC)
local f_usTmOffset = ProtoField.uint32("TimeOffset", "TimeOffset", base.DEC)
local f_usTmSec = ProtoField.uint32("TimeSec", "TimeSec", base.DEC)
local f_usTmNs = ProtoField.uint32("TimeNs", "TimeNs", base.DEC)


local f_Source_eNBID = ProtoField.uint32("Source_eNBID", "Source_eNBID", base.DEC)
local f_Destination_eNBID = ProtoField.uint32("Destination_eNBID", "Destination_eNBID", base.DEC)

--uu专用包头
local f_eNB_ID = ProtoField.uint32("eNBID", "eNBID", base.DEC)
local f_Cell_ID = ProtoField.uint16("Cell-ID", "Cell-ID", base.DEC)
local f_Channel_Type = ProtoField.uint8("ChannelType", "ChannelType", base.DEC,channelTypeArr)
local f_C_RNTI = ProtoField.uint16("C-RNTI", "C-RNTI", base.DEC)
local f_MME_UE_S1AP_ID = ProtoField.uint32("MME-UE-S1AP-ID", "MME-UE-S1AP-ID", base.DEC)
local f_MMEGroupID = ProtoField.uint16("MMEGroupID", "MMEGroupID", base.DEC)
local f_MMECode = ProtoField.uint8("MME-Code", "MME-Code", base.DEC)
local f_timeStamp = ProtoField.uint32("TimeStamp", "TimeStamp", base.DEC)
local f_SamlingType = ProtoField.uint8("SAMPLING-Type", "Sampling-Type", base.DEC)
local f_ParaCount = ProtoField.uint8("PRAR-Count", "Para-Count", base.DEC)
local f_ParaCode = ProtoField.uint16("PRAR-Code", "PRAR-Code", base.HEX)
local f_ParaValue = ProtoField.bytes("PRAR-Value", "PRAR-Value", base.HEX)
local f_LoginID = ProtoField.string("LoginID", "LoginID")
local f_Digest = ProtoField.string("Digest", "Digest")
local f_result = ProtoField.uint8("Result", "Result", base.DEC)

my_proto.fields = { f_usMsgLen, f_usMsgType, f_SequenceId, f_TotalContents,f_CompressFlag,
f_usPktLen,f_usVersion,f_usLinkType,f_usCardType,f_usCardID,f_usTmOffset,f_usTmSec,f_usTmNs,
f_abisPortNo,f_abidRate,f_abisTs,f_abisE1,f_abisSTM1,f_abisPortType,f_abisDiretion,f_Direction,
f_Interface,f_NetElement,f_C_RNTI,f_Cell_ID,f_Channel_Type,f_MME_UE_S1AP_ID,f_MMEGroupID,f_MMECode,
f_btPktLen,f_eNB_ID,f_Source_eNBID,f_Destination_eNBID,f_timeStamp,f_SamlingType,f_ParaCount,f_ParaCode,f_ParaValue,
f_LoginID, f_Digest, f_result}

sdtpv2Header = 10
comHeader = 13
uuHeader =17


function parseX2Header(buffer,pinfo,tree)
	local offset = 0
	local buffer_len = buffer:len()
	local x2HeaderTree = tree:add(my_proto, buffer(0,buffer_len), "X2 Special Header")

	x2HeaderTree:add(f_Source_eNBID,buffer(offset,4))
	offset = offset + 4;
	
	x2HeaderTree:add(f_Destination_eNBID,buffer(offset,4))
	offset = offset + 4;	
	
	x2HeaderTree:add(f_Cell_ID,buffer(offset,2))
	offset = offset + 2;
   	
    return  
end  

function parseUuHeader(buffer,pinfo,tree)
	local offset = 0
	local buffer_len = buffer:len()
	local uuHeaderTree = tree:add(my_proto, buffer(0,buffer_len), "Uu Special Header")
	
	local channel_type = 0xff;

	uuHeaderTree:add(f_eNB_ID,buffer(offset,4))
	offset = offset + 4;
	
	uuHeaderTree:add(f_Cell_ID,buffer(offset,2))
	offset = offset + 2;

	channel_type = buffer(offset,1):uint()
	uuHeaderTree:add(f_Channel_Type,buffer(offset,1))
	offset = offset + 1;

	uuHeaderTree:add(f_C_RNTI,buffer(offset,2))
    offset = offset + 2;
	
	uuHeaderTree:add(f_MME_UE_S1AP_ID,buffer(offset,4))
    offset = offset + 4;
	
	uuHeaderTree:add(f_MMEGroupID,buffer(offset,2))
    offset = offset + 2;
	

	uuHeaderTree:add(f_MMECode,buffer(offset,1))
    offset = offset + 1;
    	
    	
    return  channel_type
end  

function parseUuExtendHeader(buffer,pinfo,tree)
	local offset = 0
	local samplingtype = 0
	local buffer_len = buffer:len()
	local uuExtendHeaderTree = tree:add(my_proto, buffer(0,buffer_len), "UuExtend Special Header")
	
	samplingtype = buffer(offset,1):uint()
	uuExtendHeaderTree:add(f_SamlingType,buffer(offset,1))
	offset = offset + 1;

	uuExtendHeaderTree:add(f_eNB_ID,buffer(offset,4))
	offset = offset + 4;
	
	uuExtendHeaderTree:add(f_Cell_ID,buffer(offset,2))
	offset = offset + 2;

	if samplingtype == 0x02 then 

		uuExtendHeaderTree:add(f_C_RNTI,buffer(offset,2))
		offset = offset + 2;
		
		uuExtendHeaderTree:add(f_MME_UE_S1AP_ID,buffer(offset,4))
		offset = offset + 4;
		
		uuExtendHeaderTree:add(f_MMEGroupID,buffer(offset,2))
		offset = offset + 2;
		
	
		uuExtendHeaderTree:add(f_MMECode,buffer(offset,1))
		offset = offset + 1;
	end
	--pinfo.cols.info:set("uu-extend")
    	
end  

function parsePayload(buffer,pinfo,tree,interfacetype,channel_type,treeroot)
	local offset = 0
	local buffer_len = buffer:len()
	local payloadTree = tree:add(my_proto, buffer(0,buffer_len), "SDTP Payload")
	local uu_dis = 0xff;
	
	if interfacetype == 0x01 then 
	    if channel_type == 0x00 then
			uu_dis = Dissector.get("lte_rrc.bcch_bch")
		elseif channel_type == 0x01 then
			uu_dis = Dissector.get("lte_rrc.bcch_dl_sch")
		elseif channel_type == 0x02 then
			uu_dis = Dissector.get("lte_rrc.mcch")		
		elseif channel_type == 0x03 then
			uu_dis = Dissector.get("lte_rrc.pcch")
		elseif channel_type == 0x04 then
			uu_dis = Dissector.get("lte_rrc.dl_ccch")
		elseif channel_type == 0x05 then
			uu_dis = Dissector.get("lte_rrc.dl_dcch")		
		elseif channel_type == 0x06 then
			uu_dis = Dissector.get("lte_rrc.ul_ccch")	
		elseif channel_type == 0x07 then
			uu_dis = Dissector.get("lte_rrc.ul_dcch")		
	        end	
		uu_dis:call(buffer(0,buffer_len):tvb(),pinfo,treeroot)
	elseif interfacetype == 0x03 then
	    local x2ap_dis = Dissector.get("x2ap")
		x2ap_dis:call(buffer(0,buffer_len):tvb(),pinfo,treeroot)	
	
	elseif interfacetype == 0x02 then
		local paravalue = buffer(offset,1):uint()
		local uuextendTree = treeroot:add(my_proto, buffer(0,buffer_len), string.format("LTE UU-Extend protocal  ParaCount(%d) ",paravalue))
		
	    	uuextendTree:add(f_ParaCount,buffer(offset,1))
		offset = offset + 1
		
		local index = 1
		local paracode = 0
		while index <= paravalue do
			paracode = buffer(offset,2):uint()
			uuextendTree:add(f_ParaCode,buffer(offset,2))
			offset = offset + 2
			
			if (paracode == 0x0101) or (paracode == 0x0104) then
				uuextendTree:add(f_ParaValue,buffer(offset,1))
				offset = offset + 1
			elseif (paracode == 0x0102) or (paracode == 0x0103) then
				uuextendTree:add(f_ParaValue,buffer(offset,9))
				offset = offset + 9
			elseif (paracode == 0x0105) or (paracode == 0x0106)  or (paracode == 0x0108) then
				uuextendTree:add(f_ParaValue,buffer(offset,2))
				offset = offset + 2
			elseif paracode == 0x0107 then
				uuextendTree:add(f_ParaValue,buffer(offset,12))
				offset = offset + 12
			elseif (paracode >= 0x0201) and (paracode <= 0x0208) then
				uuextendTree:add(f_ParaValue,buffer(offset,8))
				offset = offset + 8
			elseif (paracode >= 0x0301) and (paracode <= 0x0319) then
				uuextendTree:add(f_ParaValue,buffer(offset,4))
				offset = offset + 4
			elseif paracode == 0x031A then
				uuextendTree:add(f_ParaValue,buffer(offset,2))
				offset = offset + 2
			elseif (paracode == 0x031B) or (paracode == 0x031C) then
				uuextendTree:add(f_ParaValue,buffer(offset,8))
				offset = offset + 8
			elseif (paracode >= 0x031D) and (paracode <= 0x031F) then
				uuextendTree:add(f_ParaValue,buffer(offset,1))
				offset = offset + 1
			elseif (paracode >= 0x0401) and (paracode <= 0x0407) then
				uuextendTree:add(f_ParaValue,buffer(offset,2))
				offset = offset + 2
			end
			
			index = index+1
		end	
	end
	
    return  
end   


function parseCommHeader(buffer,pinfo,tree)
	local offset = 0
	local buffer_len = buffer:len()
	local commonHeaderTree = tree:add(my_proto, buffer(0,buffer_len), "Common Header")
	
	commonHeaderTree:add(f_usVersion,buffer(offset,1)) 
	offset = offset + 1;
	
	CardType = buffer(offset,1):uint()
	commonHeaderTree:add(f_usCardType,buffer(offset,1))
	offset = offset + 1;

	local tmSec = buffer(offset, 4):uint()
	local tmNs  = buffer(offset + 4, 4):uint()
	
	--object ntm = NSTime(tmSec, tmNs)
	--pinfo.cols.info:set(ntm.__tostring())
	
	--local tmTree = tree:add(f_timeStamp,buffer(offset,8),nstime_t:__tostring())
	--commonHeaderTree:add("tttt",buffer(offset,8))
	
	commonHeaderTree:add(f_usTmSec,buffer(offset,4))
    offset = offset + 4;
	
	commonHeaderTree:add(f_usTmNs,buffer(offset,4))
    offset = offset + 4;
    	
	commonHeaderTree:add(f_NetElement,buffer(offset,1))
	offset = offset + 1; 
	
	interfacetype = buffer(offset,1):uint()
	commonHeaderTree:add(f_Interface,buffer(offset,1))
	offset = offset + 1; 
	
	commonHeaderTree:add(f_Direction,buffer(offset,1))
	offset = offset + 1;   	
    return interfacetype
end    
 

function parsePacket(buffer,pinfo,tree,treeroot)
	local buffer_len = buffer:len()
	tree:add(f_usPktLen, buffer(0,2))
	local interfacetype = parseCommHeader(buffer(2,comHeader),pinfo,tree)
	local channel_type = 0xff;
	
	local spciheadlen = buffer(2+comHeader, 1):uint()
	tree:add(f_btPktLen, buffer(2+comHeader, 1))
	
	if interfacetype == 0x01 then 
		channel_type = parseUuHeader(buffer(2+comHeader+1,spciheadlen),pinfo,tree)
	elseif interfacetype == 0x02 then
		parseUuExtendHeader(buffer(2+comHeader+1,spciheadlen),pinfo,tree)
	elseif interfacetype == 0x03 then
		parseX2Header(buffer(2+comHeader+1,spciheadlen),pinfo,tree)
	end
	
	parsePayload(buffer(2+comHeader+1+spciheadlen,buffer_len-2-comHeader-1-spciheadlen),pinfo,tree,interfacetype,channel_type,treeroot)
	
	return 
end

function parseHeader(buffer,pinfo,tree)
	local offset = 0
	local buffer_len = buffer:len()
	local sdtpHeaderTree = tree:add(my_proto, buffer(0, sdtpv2Header), "SDTP Header")
	sdtpHeaderTree:add(f_usMsgLen, buffer(offset,2))
	offset = offset + 2;
	
	usMsgType = buffer(offset,2):uint()
	pinfo.cols.info:set(msgAry[usMsgType])
	sdtpHeaderTree:add(f_usMsgType,buffer(offset,2)) 
	offset = offset + 2;
	
	sdtpHeaderTree:add(f_SequenceId,buffer(offset,4))
	offset = offset + 4;
	
	usContent = buffer(offset,1):uint()
	sdtpHeaderTree:add(f_TotalContents,buffer(offset,1))
	offset = offset + 1;
	
	usCompressFlag = buffer(offset,1):uint()
	sdtpHeaderTree:add(f_CompressFlag,buffer(offset,1))
    offset = offset + 1;
 	   	
    return  usMsgType ,usContent,usCompressFlag
end    

--[[Auth process:
	auth only for SCA IF1, because there so many auth-method
	loginID = 12B,
	Digest	= 16B,
	Timestamp= 4B,
]]

function parseAuth(buffer,pinfo,tree,MsgType,len)
	if MsgType == 0x0002 then
		if len == 32 then
			local loginID = buffer(0, 12):string()
			tree:add(f_LoginID,buffer(0,12))
			local digest = buffer(12, 16):string()
			tree:add(f_Digest,buffer(12,16))
			
			local tmSec = buffer(28, 4):uint()
			tree:add(f_timeStamp,buffer(28,4))
		end
	elseif MsgType == 0x8002 then
		if len == 1 then
			local result = buffer(0,1):uint()
			tree:add(f_result,buffer(0,1))
		end
	end
	return
end

function parseBody(buffer,pinfo,tree,MsgType,Content,CompressFlag,treeroot)
	local offset = 0
	local pktlen = 0;
	local index = 1;
	local buffer_len = buffer:len()
	local left_len = buffer_len;
	if buffer_len == 0 then return end;
	local sdtpBodyTree = tree:add(my_proto, buffer(0,buffer_len), "Body of SDTP")
	if MsgType==5 then
		while index <= Content do
			if(left_len < 2) then break end
			pktlen = buffer(offset,2):uint()
			
			if(left_len < pktlen+2) then break end
			sdtpPacketTree = sdtpBodyTree:add(my_proto, buffer(offset,pktlen+2), string.format("SDTP PACKET %2d ",index))
			parsePacket(buffer(offset,pktlen+2),pinfo,sdtpPacketTree,treeroot)
			offset = offset + pktlen+2
			left_len = left_len-pktlen-2
			index = index+1
		end
	elseif MsgType == 0x0002 or MsgType == 0x8002 then
		parseAuth(buffer, pinfo, sdtpBodyTree, MsgType, buffer_len)
		
	end
    return
end  

function my_proto.dissector(buffer,pinfo,tree)
	pinfo.cols.protocol:set("sdtp")
	local offset = 0
	local buffer_len = buffer:len()
	local left_len = buffer_len;
	local usBodyLen = 0
	
	while(1)	do
		if(left_len < 2) then break end
		--total length
		usBodyLen = buffer(offset,2):uint()
		if(left_len < usBodyLen) then break end
		--message type 
		usMsgType = buffer(offset+2,2):uint()
		if(msgAry[usMsgType] == nil ) then return end
		
		usSequence = buffer(offset+4,4):uint()
		local myProtoTree = tree:add(my_proto, buffer(offset, usBodyLen), string.format("SDTP Protocol Sequence  %d",usSequence))
		t,ct,cm = parseHeader(buffer(offset, sdtpv2Header),pinfo,myProtoTree)
		parseBody(buffer(offset+sdtpv2Header, usBodyLen-sdtpv2Header),pinfo,myProtoTree,t,ct,cm,tree)
		offset = offset + usBodyLen
		left_len = left_len-usBodyLen
	end	
	
	if(left_len < usBodyLen and left_len ~=0)  then
		pinfo.desegment_len = usBodyLen - left_len
		pinfo.desegment_offset = offset
	end
	
	
    return
end     
        
local tcp_port_table = DissectorTable.get("tcp.port")
for i,port in ipairs{8000,8001,8002,8003,8004} do
	tcp_port_table:add(port,my_proto)
end